/*
   Copyright 2009 Bloo. All Rights Reserved.

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/
package org.youtensils.www.client.widgets;

import java.util.ArrayList;

import org.youtensils.www.client.data.DownloadItem;
import org.youtensils.www.client.events.DownloadListener;

import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.VerticalPanel;

/**
 * A download manager widget that offers progress tracking for multiple downloads.
 * @author Bobby Soares
 */
public class DownloadManager extends Composite {
	private ArrayList<DownloadListener> listeners;
	private VerticalPanel contentPane;
	
	/**
	 * The default constructor.
	 */
	public DownloadManager(){
		this.listeners = new ArrayList<DownloadListener>();
		this.contentPane = new VerticalPanel();
		this.contentPane.setHorizontalAlignment(VerticalPanel.ALIGN_LEFT);
		this.contentPane.setVerticalAlignment(VerticalPanel.ALIGN_TOP);
    	initWidget(this.contentPane);
    	setStyleName("gwt-DownloadManager");
	}
	
	/**
	 * Adds an event listener.
	 * @param listener the event listener to register
	 */
	public void addListener(DownloadListener listener){
		this.listeners.add(listener);
	}
	
	/**
	 * Adds a download item to this download manager
	 * @param downloadId the download id
	 * @param format the download format
	 * @param name the download name
	 */
	public void addDownload(String downloadId, String format, String name){
		DownloadTracker track = new DownloadTracker(downloadId, this);
		track.setProgressInformation(0, DownloadTracker.QUEUED_STATUS, format, name, "0Kb", "0Kb", "Waiting");
		this.contentPane.add(track);
	}
	
	/**
	 * Cancels a download.
	 * @param downloadId the id of the download which should be canceled
	 */
	public void cancelDownload(String downloadId){
		for(int i=0; i<this.listeners.size(); i++){
			this.listeners.get(i).onCancelDownload(this, downloadId);
		}
		DownloadTracker track = getDownloadTracker(downloadId);
		if(track != null) track.removeFromParent();
	}
	
	/**
	 * Removes a download.
	 * @param downloadId the id of the download which should be removed.
	 */
	public void removeDownload(String downloadId){
		DownloadTracker track = getDownloadTracker(downloadId);
		if(track != null) track.removeFromParent();
	}
	
	/**
	 * Updates the progress information for a given download.
	 * @param downloadId the id of the download which is being updated
	 * @param percentage a percentage value indicating how close to completion the download is
	 * @param loaded the number of bytes that have loaded for this download
	 * @param total the total number of bytes for this download
	 * @param action a value describing the current action
	 */
	public void setDownloadProgressInformation(String downloadId,
			int percentage, String loaded, String total, String action){
		DownloadTracker track = this.getDownloadTracker(downloadId);
		if(track == null) return;
		track.setProgressInformation(percentage, DownloadTracker.ACTIVE_STATUS, track.getDownloadFormat(),
				track.getDownloadName(), loaded, total, action);
	}
	
	/**
	 * Retrieves the next waiting download item in the stack.
	 * @return the next download item
	 */
	public DownloadItem getNextDownload(){
		for(int i=0; i<this.contentPane.getWidgetCount(); i++){
			DownloadTracker track = (DownloadTracker)this.contentPane.getWidget(i);
			if(track.getProgressStatus().equals(DownloadTracker.QUEUED_STATUS)){
				track.setProgressStatus(DownloadTracker.ACTIVE_STATUS);
				return new DownloadItem(track.getDownloadId(), track.getDownloadFormat());
			}
		}
		return null;
	}
	
	/**
	 * Retrieves a value indicating whether there is an active download in this download manager.
	 * @return whether there is an active download in the download item stack
	 */
	public boolean hasActiveDownload(){
		for(int i=0; i<this.contentPane.getWidgetCount(); i++){
			DownloadTracker track = (DownloadTracker)this.contentPane.getWidget(i);
			if(track.getProgressStatus().equals(DownloadTracker.ACTIVE_STATUS)){
				return true;
			}
		}
		return false;
	}
	
	/**
	 * Moves a download item up in the download item stack
	 * @param downloadId the download id of the item to be moved up
	 * @return the new position of the download
	 */
	public int increaseDownloadPriority(String downloadId){
		DownloadTracker track = this.getDownloadTracker(downloadId);
		if(track == null) return -1;
		return increaseDownloadPriority(track);
	}
	
	/**
	 * Moves a download item up in the download item stack
	 * @param track the download tracker of the item to be moved up
	 * @return the new position of the download
	 */
	public int increaseDownloadPriority(DownloadTracker track){
		int ci = this.contentPane.getWidgetIndex(track);
		if(ci == 0) return 0;
		this.contentPane.insert(track, ci-1);
		return ci - 1;
	}
	
	/**
	 * Moves a download item down in the download item stack
	 * @param downloadId the download id of the item to be moved down
	 * @return the new position of the download
	 */
	public int decreaseDownloadPriority(String downloadId){
		DownloadTracker track = this.getDownloadTracker(downloadId);
		if(track == null) return -1;
		else return decreaseDownloadPriority(track);
	}
	
	/**
	 * Moves a download item down in the download item stack
	 * @param downloadId the download tracker of the item to be moved down
	 * @return the new position of the download
	 */
	public int decreaseDownloadPriority(DownloadTracker track){
		int ci = this.contentPane.getWidgetIndex(track);
		if(ci == this.contentPane.getWidgetCount() - 1) return this.contentPane.getWidgetCount() - 1;
		this.contentPane.insert(track, ci+2);//+2 because insert receives the index before which the widget should be inserted
		return ci + 1;
	}
	
	/**
	 * Retrieves the DownloadTracker widget for a given download item.
	 * @param downloadId the download id
	 * @return the DownloadTracker associated with the download
	 */
	public DownloadTracker getDownloadTracker(String downloadId){
		for(int i=0; i<this.contentPane.getWidgetCount(); i++){
			DownloadTracker track = (DownloadTracker)this.contentPane.getWidget(i);
			if(track.getDownloadId().equals(downloadId)){
				return track;
			}
		}
		return null;
	}
}
